import React, { Component } from 'react';
import PropTypes from 'prop-types';
import AssetSvg from './images/asset.svg';
import Thrend from './components/thrend';
import BugSvg from './images/bug.svg';
import FingerSvg from './images/finger.svg';
import Top10 from './components/top10';
import { getStatics } from '@api/task';
import { LEVEL_TEXT, LEVEL_COLOR, LEVEL_TYPES } from '@util/constants';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import _ from 'lodash';
import moment from 'moment';
import { getTop10Vuls } from '@api/task';
import { getAssetStats } from '@api/asset';
import cn from 'classnames';
import ExportModal from '@components/export-modal';
import {
  getAssetAnalysisReportsList,
  exportAssetAnalysisReport,
  downloadAssetAnalysisReport,
  deleteAssetAnalysisReport
} from '@api/asset';
import { Spin, Icon } from 'antd';
import { actions as assetManageActions } from '../asset-manage/reducer';
import './index.less';

const Card = props => {
  // eslint-disable-next-line
  const { name, color, value, onClick } = props;
  return (
    <div
      className={cn('card', { 'card-clickable': typeof onClick === 'function' })}
      onClick={e => typeof onClick === 'function' && onClick(e)}
    >
      <div className="card__top">{name}</div>
      <div className="card__bottom" style={{ color }}>
        <span className="value">{value}</span>
      </div>
    </div>
  );
};

class AggregateAnalysis extends Component {
  constructor(props) {
    super(props);
    this.state = {
      realtime: {},
      statics: [],
      onedayLanched: [],
      exportModalVisible: false,
      loading: false,
      hasOneDayModule: false
    };

    this.top10VulsCache = {};
    this.top10FingersCache = {};
  }

  componentDidMount() {
    const { modules = [] } = this.props;
    const hasOneDayModule = modules.includes('1day');
    this.setState({
      hasOneDayModule
    });
    this.loadStatics();
  }

  loadStatics = () => {
    this.setState({ loading: true });
    getStatics()
      .then(res => {
        const realtime = _.get(res, 'data.data.realtime', {});
        const statics = _.get(res, 'data.data.statics', []);
        const onedayLanched = _.get(res, 'data.data.oneday_lanched_trends', []);

        this.setState({
          realtime,
          statics,
          onedayLanched
        });
      })
      .finally(() => {
        this.setState({ loading: false });
      });
  };

  getTop10Fingers = (key, callback) => {
    if (typeof callback !== 'function') {
      return;
    }
    if (this.top10FingersCache[key]) {
      return callback(this.top10FingersCache[key]);
    }
    getAssetStats(key).then(res => {
      const stats = _.get(res, 'data.data.stats') || {};
      const topAssetCategory = _.get(stats, 'assetCategory', []);
      const assetCategory = _.sortBy(_.flatten(_.map(topAssetCategory, 'children')), 'count')
        .reverse()
        .filter(Boolean);
      const os = _.sortBy(
        _.flattenDeep(_.map(_.get(stats, 'os', []), item => _.map(item.children, 'children', []))),
        'count'
      )
        .reverse()
        .filter(Boolean);
      const service = _.sortBy(
        _.flattenDeep(_.map(_.get(stats, 'service', []), 'children')),
        'count'
      )
        .reverse()
        .filter(Boolean);

      // 保留端口中的0端口号使其正常显示, 并过滤不合规的值
      const port = _.get(stats, 'port', [])
        .filter(p => {
          return typeof p.name === 'number' && !isNaN(p.name);
        })
        .slice(0, 10)
        .map(p => {
          return {
            ...p,
            name: _.get(p, 'name', '').toString()
          };
        });

      this.top10FingersCache = {
        topAssetCategory: topAssetCategory.slice(0, 10),
        assetCategory: assetCategory.slice(0, 10),
        os: os.slice(0, 10),
        port: port,
        service: service.slice(0, 10),
        component: _.get(stats, 'component', []).slice(0, 10),
        waf: _.get(stats, 'waf', []).slice(0, 10)
      };
      callback(this.top10FingersCache[key] || []);
    });
  };

  getTop10Vuls = (key, callback) => {
    if (typeof callback !== 'function') {
      return;
    }
    if (this.top10VulsCache[key]) {
      callback(this.top10VulsCache[key]);
    }
    getTop10Vuls(key).then(res => {
      const top10 = _.get(res, 'data.data.vul_sites', []);
      top10.forEach(item => (item.value = item.count));
      this.top10VulsCache[key] = top10;
      callback(top10);
    });
  };

  jumpToAllSites = () => {
    this.props.assetManageActions.resetQuery({});
    this.props.history.push('/asset-manage');
  };

  jumpToDomesticSites = () => {
    // 国产化统计需要精准搜索
    this.props.assetManageActions.resetQuery({ search: 'exact:tag="国产化"' });
    this.props.history.push('/asset-manage');
  };

  jumpToPingSites = () => {
    this.props.assetManageActions.resetQuery({ search: 'pingable="active"' });
    this.props.history.push('/asset-manage');
  };

  jumpToPortSites = () => {
    this.props.assetManageActions.resetQuery({ search: 'portful="active"' });
    this.props.history.push('/asset-manage');
  };

  jumpToSiteByOrigin = origin => {
    this.props.assetManageActions.resetQuery({ search: `origin="${origin}"` });
    this.props.history.push('/asset-manage');
  };

  jumpToVulAll = (list = []) => {
    this.props.assetManageActions.resetQuery({
      search: list.map(k => `vulLevel="${k}"`).join(' || ')
    });
    this.props.history.push('/asset-manage');
  };

  jumpToOnedayLanched = e => {
    const monthDays = moment(e.name).daysInMonth(),
      month = e.name;
    const filter = { page: 1, lanchedStart: `${month}-01`, lanchedEnd: `${month}-${monthDays}` };
    sessionStorage.setItem('filter_oneDayList', JSON.stringify(filter));
    this.props.history.push('/vul/oneday-detection');
  };

  onClickVulTop10Item = ({ value }) => {
    this.props.assetManageActions.resetQuery({ search: `exact:vul="${value}"` });
    this.props.history.push('/asset-manage');
  };

  onClickFingerTop10Item = ({ activeKey, value }) => {
    const searchKey = activeKey === 'topAssetCategory' ? 'assetCategory' : activeKey;
    this.props.assetManageActions.resetQuery({ search: `exact:${searchKey}="${value}"` });
    this.props.history.push('/asset-manage');
  };

  // 渲染总资产数图
  renderAllSite = () => {
    const { realtime, loading, hasOneDayModule } = this.state;
    const { modules = [] } = this.props;
    const assetsCount = _.get(realtime, 'sites', 0);
    const domesticCount = _.get(realtime, 'domesticCount', 0);
    const domesticRateValue = domesticCount / assetsCount;
    const domesticRate = assetsCount > 0 ? Math.round(domesticRateValue * 100 * 10000) / 10000 : 0;

    const portAssetsCount = _.get(realtime, 'ips.portful', 0);
    const pingAssetsCount = _.get(realtime, 'ips.pingable', 0);
    const vulAssetsCount = _.get(realtime, 'site_with_vuls', 0);
    const origin = _.get(realtime, 'ips.origin', {});
    return (
      <Spin tip="加载中" spinning={loading} wrapperClassName={cn('aa-top__left', 'block__left')}>
        <div className={`${hasOneDayModule ? 'summary' : 'summary-big'}`}>
          <div className="site-wrapper">
            <span className={cn('img-wrapper', 'asset-img')}>
              <img src={AssetSvg} />
            </span>
          </div>
          <div className="total-wrapper">
            <div className="total">
              <Card name="总资产数" value={assetsCount} onClick={this.jumpToAllSites} />
              <Card name="国产化资产数" value={domesticCount} onClick={this.jumpToDomesticSites} />
              <Card name="国产化率" value={`${domesticRate}%`} />
            </div>
            <div className="origin">
              <div onClick={() => this.jumpToSiteByOrigin('active')}>
                主动探测的资产数&nbsp;&nbsp;{_.get(origin, 'active', 0)}
              </div>
              <div onClick={() => this.jumpToSiteByOrigin('hand')}>
                人工添加的资产数&nbsp;&nbsp;{_.get(origin, 'hand', 0)}
              </div>
              <div onClick={() => this.jumpToSiteByOrigin('switch')}>
                交换机采集的资产数&nbsp;&nbsp;{_.get(origin, 'switch', 0)}
              </div>
              {modules.includes('Passive-asset-detection') && (
                <div onClick={() => this.jumpToSiteByOrigin('passive')}>
                  被动探测的资产数&nbsp;&nbsp;{_.get(origin, 'passive', 0)}
                </div>
              )}
              {modules.includes('Internet-asset-query') && (
                <div onClick={() => this.jumpToSiteByOrigin('zoomeye.org')}>
                  公网查询的资产数&nbsp;&nbsp;{_.get(origin, 'zoomeye.org', 0)}
                </div>
              )}
            </div>
          </div>
        </div>
        <div className={`${hasOneDayModule ? 'items' : 'items-big'}`}>
          <Card name="有存活端口的资产数" value={portAssetsCount} onClick={this.jumpToPortSites} />
          <Card name="ping有响应的资产数" value={pingAssetsCount} onClick={this.jumpToPingSites} />
          {hasOneDayModule && (
            <Card
              name="漏洞影响资产数"
              value={vulAssetsCount}
              onClick={() => this.jumpToVulAll(['fatal', 'high', 'middle', 'low'])}
            />
          )}
        </div>
      </Spin>
    );
  };

  // 渲染总漏洞数图
  renderAllVul = () => {
    const { realtime, loading } = this.state;
    return (
      <Spin tip="加载中" spinning={loading} wrapperClassName={cn('aa-top__right', 'block__right')}>
        <div>
          <div className="summary">
            <span className={cn('img-wrapper', 'bug-img')}>
              <img src={BugSvg} />
            </span>

            <Card
              name="当前漏洞总数"
              value={LEVEL_TYPES.reduce((all, type) => {
                return all + _.get(realtime, `vuls.${type}`, 0);
              }, 0)}
              onClick={() => this.jumpToVulAll(['fatal', 'high', 'middle', 'low'])}
            />
          </div>
          <div className="items">
            {LEVEL_TYPES.map(type => (
              <Card
                name={`当前${LEVEL_TEXT[type]}漏洞`}
                value={_.get(realtime, `vuls.${type}`, 0)}
                key={type}
                color={LEVEL_COLOR[type]}
                onClick={() => this.jumpToVulAll([type])}
              />
            ))}
          </div>
        </div>
      </Spin>
    );
  };

  // 渲染变化趋势图
  renderThrendChange = () => {
    const { loading, statics, onedayLanched, hasOneDayModule } = this.state;
    const vulDates = [];
    const sitesThrend = {
      name: '资产数量',
      color: '#1990FE',
      data: []
    };
    const vulSitesThrend = {
      name: '漏洞影响资产数',
      color: LEVEL_COLOR['high'],
      data: []
    };
    const allVulsThrend = {
      name: '漏洞总数',
      color: '#1990FE',
      data: []
    };
    const fatalVulsThrend = {
      name: '严重漏洞',
      color: LEVEL_COLOR['fatal'],
      data: []
    };
    const highVulsThrend = {
      name: '高危漏洞',
      color: LEVEL_COLOR['high'],
      data: []
    };
    const middleVulsThrend = {
      name: '中危漏洞',
      color: LEVEL_COLOR['middle'],
      data: []
    };
    const lowVulsThrend = {
      name: '低危漏洞',
      color: LEVEL_COLOR['low'],
      data: []
    };

    const totalAssetsThrend = {
      name: '总数',
      color: '#1990FE',
      data: []
    };
    const portAssetsThrend = {
      name: '有存活端口',
      color: '#11AB4E',
      data: []
    };
    const pingAssetsThrend = {
      name: 'PING_有响应',
      color: '#F67E23',
      data: []
    };
    const bySwitchAsssetsTrend = {
      name: '交换机添加',
      color: '#FED831',
      data: []
    };
    const onedayLanchedThrend = {
      name: '1DAY漏洞数量',
      color: LEVEL_COLOR['low'],
      data: onedayLanched.map(i => _.get(i, 'count')).reverse()
    };
    const onedayLanchedDates = onedayLanched.map(i => _.get(i, '_id')).reverse();

    statics.forEach(item => {
      const date = _.get(item, 'date_format');
      if (!date || vulDates.indexOf(date) !== -1) {
        return;
      }
      vulDates.unshift(date);

      sitesThrend.data.unshift(_.get(item, 'sites', 0));
      vulSitesThrend.data.unshift(_.get(item, 'site_with_vuls', 0));

      const { fatal = 0, high = 0, middle = 0, low = 0 } = _.get(item, 'vuls', {});
      allVulsThrend.data.unshift(fatal + high + middle + low);
      fatalVulsThrend.data.unshift(fatal);
      highVulsThrend.data.unshift(high);
      middleVulsThrend.data.unshift(middle);
      lowVulsThrend.data.unshift(low);

      const { pingable = 0, portful = 0, bySwitch = 0, total = 0 } = _.get(item, 'ips', {});
      totalAssetsThrend.data.unshift(total);
      pingAssetsThrend.data.unshift(pingable);
      portAssetsThrend.data.unshift(portful);
      bySwitchAsssetsTrend.data.unshift(bySwitch);
    });
    const assetThrend = (
      <div className={cn('aa-middle', 'block__left')}>
        <Thrend
          flag="asset_thrend"
          title="资产变化趋势"
          dates={vulDates}
          data={hasOneDayModule ? [sitesThrend, vulSitesThrend] : [sitesThrend]}
          loading={loading}
        />
      </div>
    );
    const vulThrend = hasOneDayModule && (
      <div className={cn('aa-middle', 'block__right')}>
        <Thrend
          flag="vul_thrend"
          title="漏洞变化趋势"
          dates={vulDates}
          data={[allVulsThrend, fatalVulsThrend, highVulsThrend, middleVulsThrend, lowVulsThrend]}
          loading={loading}
        />
      </div>
    );
    const usefulThrend = (
      <div className={cn('aa-middle', 'block__left')}>
        <Thrend
          flag="useful_thrend"
          title="可用性变化趋势"
          dates={vulDates}
          data={[totalAssetsThrend, portAssetsThrend, pingAssetsThrend, bySwitchAsssetsTrend]}
          loading={loading}
        />
      </div>
    );
    const onedayThrend = hasOneDayModule && (
      <div className={cn('aa-middle', 'block__right')}>
        <Thrend
          flag="oneday_thrend"
          title="1DAY漏洞爆发趋势"
          dates={onedayLanchedDates}
          data={[onedayLanchedThrend]}
          loading={loading}
          clickEvent={e => this.jumpToOnedayLanched(e)}
        />
      </div>
    );
    return {
      assetThrend,
      vulThrend,
      usefulThrend,
      onedayThrend
    };
  };

  renderFingerTop10 = () => {
    const { history } = this.props;
    return (
      <Top10
        title="指纹信息 Top10 统计"
        icon={FingerSvg}
        iconClassName="finger-img"
        className="block__left"
        loader={this.getTop10Fingers}
        getItemColor={() => '#0B6FD6'}
        nameField="name"
        valueField="count"
        history={history}
        onClickItem={this.onClickFingerTop10Item}
        items={[
          { key: 'topAssetCategory', name: '资产分类(大类)' },
          { key: 'assetCategory', name: '资产分类(细分)' },
          { key: 'os', name: '操作系统' },
          { key: 'port', name: '端口' },
          { key: 'service', name: '服务' },
          { key: 'component', name: '组件' },
          { key: 'waf', name: '防护信息' }
        ]}
      />
    );
  };

  renderVulTop10 = () => {
    const { history } = this.props;
    return (
      <Top10
        title="漏洞影响资产数 Top10 统计"
        icon={BugSvg}
        iconClassName="bug-img"
        className="block__right"
        loader={this.getTop10Vuls}
        history={history}
        getItemColor={({ level = 'low' }) => LEVEL_COLOR[level]}
        onClickItem={this.onClickVulTop10Item}
        items={[
          { key: 'all', name: '全部漏洞' },
          { key: 'fatal', name: '严重漏洞' },
          { key: 'high', name: '高危漏洞' },
          { key: 'middle', name: '中危漏洞' },
          { key: 'low', name: '低危漏洞' }
        ]}
      />
    );
  };

  render() {
    const { assetThrend, vulThrend, usefulThrend, onedayThrend } = this.renderThrendChange();
    const { exportModalVisible, hasOneDayModule } = this.state;

    let exportModalConfig = {
      target: '综合分析报告',
      enableRangePick: false,
      onVisibleChange: () => this.setState({ exportModalVisible: false }),
      listLoader: getAssetAnalysisReportsList,
      downloadDispatcher: exportAssetAnalysisReport,
      downloader: downloadAssetAnalysisReport,
      deleter: deleteAssetAnalysisReport
    };

    return (
      <div className="aggregate-analysis">
        <div className="aa-title">
          <div>
            <span className="title">综合分析</span>
            <a className="download-btn" onClick={() => this.setState({ exportModalVisible: true })}>
              <Icon type="download" />
              导出综合分析报告
            </a>
          </div>
          <span
            className="return-asset-list"
            onClick={() => {
              this.props.history.push('/asset-manage');
            }}
          >
            <Icon type="export" />
            &nbsp;返回资产列表
          </span>
        </div>
        {hasOneDayModule ? (
          <div className="aa-wrapper">
            <div className="aa-box padding-right-24">{this.renderAllSite()}</div>
            <div className="aa-box">{this.renderAllVul()}</div>
            <div className="aa-box padding-right-24">{assetThrend}</div>
            <div className="aa-box">{vulThrend}</div>
            <div className="aa-box padding-right-24">{usefulThrend}</div>
            <div className="aa-box">{onedayThrend}</div>
            <div className="aa-box padding-right-24">{this.renderFingerTop10()}</div>
            <div className="aa-box">{this.renderVulTop10()}</div>
          </div>
        ) : (
          <div className="aa-wrapper">
            <div className="aa-box padding-right-24">{this.renderAllSite()}</div>
            <div className="aa-box">{this.renderFingerTop10()}</div>
            <div className="aa-box padding-right-24">{assetThrend}</div>
            <div className="aa-box">{usefulThrend}</div>
          </div>
        )}

        <ExportModal visible={exportModalVisible} {...exportModalConfig} />
      </div>
    );
  }
}

AggregateAnalysis.propTypes = {
  history: PropTypes.object,
  assetManageActions: PropTypes.object,
  modules: PropTypes.array
};

const mapStateToProps = state => {
  return {
    modules: _.get(state, 'auth.cert.modules', [])
  };
};

const mapDispatchToProps = dispatch => {
  return {
    assetManageActions: bindActionCreators(assetManageActions, dispatch)
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(AggregateAnalysis);
